using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

namespace Terra.Core.Common.Utils;

/// <summary>
/// 安全工具类
/// </summary>
public static class SecurityUtils
{
    #region Hash计算

    /// <summary>
    /// 计算MD5哈希
    /// </summary>
    /// <param name="input">输入字符串</param>
    /// <returns>MD5哈希值</returns>
    public static string ComputeMd5Hash(string input)
    {
        using var md5 = MD5.Create();
        var inputBytes = Encoding.UTF8.GetBytes(input);
        var hashBytes = md5.ComputeHash(inputBytes);
        return BitConverter.ToString(hashBytes).Replace("-", "").ToLower();
    }

    /// <summary>
    /// 计算SHA256哈希
    /// </summary>
    /// <param name="input">输入字符串</param>
    /// <returns>SHA256哈希值</returns>
    public static string ComputeSha256Hash(string input)
    {
        using var sha256 = SHA256.Create();
        var inputBytes = Encoding.UTF8.GetBytes(input);
        var hashBytes = sha256.ComputeHash(inputBytes);
        return BitConverter.ToString(hashBytes).Replace("-", "").ToLower();
    }

    #endregion

    #region AES加密解密

    /// <summary>
    /// AES加密
    /// </summary>
    /// <param name="plainText">明文</param>
    /// <param name="key">密钥(32字符)</param>
    /// <param name="iv">初始化向量(16字符)</param>
    /// <returns>加密后的字符串</returns>
    public static string AesEncrypt(string plainText, string key, string iv)
    {
        if (string.IsNullOrEmpty(plainText)) return string.Empty;

        var keyBytes = Encoding.UTF8.GetBytes(key);
        var ivBytes = Encoding.UTF8.GetBytes(iv);
        var plainBytes = Encoding.UTF8.GetBytes(plainText);

        using var aes = Aes.Create();
        aes.Key = keyBytes;
        aes.IV = ivBytes;
        aes.Mode = CipherMode.CBC;
        aes.Padding = PaddingMode.PKCS7;

        using var ms = new MemoryStream();
        using var cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write);
        cs.Write(plainBytes, 0, plainBytes.Length);
        cs.FlushFinalBlock();

        return Convert.ToBase64String(ms.ToArray());
    }

    /// <summary>
    /// AES解密
    /// </summary>
    /// <param name="cipherText">密文</param>
    /// <param name="key">密钥(32字符)</param>
    /// <param name="iv">初始化向量(16字符)</param>
    /// <returns>解密后的字符串</returns>
    public static string AesDecrypt(string cipherText, string key, string iv)
    {
        if (string.IsNullOrEmpty(cipherText)) return string.Empty;

        var keyBytes = Encoding.UTF8.GetBytes(key);
        var ivBytes = Encoding.UTF8.GetBytes(iv);
        var cipherBytes = Convert.FromBase64String(cipherText);

        using var aes = Aes.Create();
        aes.Key = keyBytes;
        aes.IV = ivBytes;
        aes.Mode = CipherMode.CBC;
        aes.Padding = PaddingMode.PKCS7;

        using var ms = new MemoryStream(cipherBytes);
        using var cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Read);
        using var sr = new StreamReader(cs);
        return sr.ReadToEnd();
    }

    #endregion

    #region 密码处理

    /// <summary>
    /// 生成随机盐值
    /// </summary>
    /// <param name="size">盐值长度</param>
    /// <returns>随机盐值</returns>
    public static string GenerateSalt(int size = 16)
    {
        var randomBytes = new byte[size];
        using var rng = RandomNumberGenerator.Create();
        rng.GetBytes(randomBytes);
        return Convert.ToBase64String(randomBytes);
    }

    /// <summary>
    /// 使用PBKDF2算法计算密码哈希
    /// </summary>
    /// <param name="password">密码</param>
    /// <param name="salt">盐值</param>
    /// <param name="iterations">迭代次数</param>
    /// <returns>密码哈希值</returns>
    public static string HashPassword(string password, string salt, int iterations = 10000)
    {
        using var pbkdf2 = new Rfc2898DeriveBytes(password, Convert.FromBase64String(salt), iterations, HashAlgorithmName.SHA256);
        var hash = pbkdf2.GetBytes(32);
        return Convert.ToBase64String(hash);
    }

    /// <summary>
    /// 验证密码
    /// </summary>
    /// <param name="password">待验证的密码</param>
    /// <param name="salt">盐值</param>
    /// <param name="hash">正确的密码哈希</param>
    /// <param name="iterations">迭代次数</param>
    /// <returns>是否验证通过</returns>
    public static bool VerifyPassword(string password, string salt, string hash, int iterations = 10000)
    {
        var computedHash = HashPassword(password, salt, iterations);
        return computedHash == hash;
    }

    #endregion
}